#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""数据库相关的模块。
"""

from os.path import join as joinpath
from logging import getLogger
from click import command, option, confirm, echo
from flask import current_app
from flask.cli import with_appcontext

from .core.entities import PRJ_UNDEPLOYED
from .core.metadata import load_all_data
from .models import db, ProjectPO, NodePO

_LOGGER = getLogger(__name__)


@command('init-db')
@option('--rebuild', is_flag=True, help='重新创建所有的表。如果指定了此标志，会删除所有已存在的表和数据，然后重新创建。')
@with_appcontext
def init_db(
        rebuild: bool = False
):
    """初始化数据库。

    创建所有必需的表，然后从元数据中同步数据。
    """
    if rebuild:
        # 如果要求重建，那么先删除所有的表。
        if confirm('将要重新创建所有的表，所有已存在的数据都将被删除且无法找回，确认继续吗？', abort=True):
            db.drop_all()
            echo('所有表和数据已被删除。')

    # 建表，如果表已存在则不创建。
    db.create_all()
    echo('已创建表。')

    projects, nodes = load_all_data(joinpath(current_app.config.root_path, 'data.yaml'))

    _update_projects_from_metadata(projects)
    echo('已同步更新项目数据。')

    _update_nodes_from_metadata(nodes)
    echo('已同步更新节点数据。')


def _update_projects_from_metadata(projects: tuple) -> None:
    """根据元数据的内容更新数据库。

    如果数据库中已存在对应的项目，那么更新。
    如果数据库中不存在对应的项目，那么创建。
    如果数据库中存在多余的项目，那么删除。

    :param projects: 待同步的项目元数据。
    """
    if not projects:
        return

    for project in projects:
        po = ProjectPO.query.filter(ProjectPO.name == project.name).first()
        if po:
            # 更新。
            po.description = project.description
            po.enabled = project.enabled
            po.set_jobs(value=project.jobs)
            po.set_nodes(value=project.nodes)

            _LOGGER.info('update project %s', project.name)
        else:
            # 创建
            po = ProjectPO(
                name=project.name,
                description=project.description,
                status=PRJ_UNDEPLOYED,
                enabled=project.enabled,
            )
            po.set_jobs(value=project.jobs)
            po.set_nodes(value=project.nodes)
            db.session.add(po)

            _LOGGER.info('create project %s', project.name)

    db.session.commit()


def _update_nodes_from_metadata(nodes: tuple) -> None:
    """根据元数据的内容更新数据库。

    如果数据库中已存在对应的节点，那么更新。
    如果数据库中不存在对应的节点，那么创建。
    如果数据库中存在多余的节点，那么删除。

    :param nodes: 待同步的节点元数据。
    """
    if not nodes:
        return None

    for node in nodes:
        po = NodePO.query.filter(NodePO.name == node.name).first()
        if po:
            # 更新
            po.description = node.description
            po.hostname = node.hostname
            po.bundle = node.bundle
            po.enabled = node.enabled
            po.set_props(value=node.props)

            _LOGGER.info('update node %s', node.name)
        else:
            # 创建
            po = NodePO(
                name=node.name,
                description=node.description,
                hostname=node.hostname,
                bundle=node.bundle,
                enabled=node.enabled
            )
            po.set_props(value=node.props)
            db.session.add(po)

            _LOGGER.info('create node %s', node.name)

    db.session.commit()
